package org.logtime.blog.service;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import org.logtime.blog.annotation.Element;
import org.logtime.blog.annotation.Key;
import org.logtime.blog.annotation.Persistent;
import org.logtime.blog.module.PageModule;
import org.logtime.blog.pojo.User;
import org.springframework.stereotype.Service;
import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Text;

/**
 * @version: 1.0
 * @Author: itmins
 * @Email: itmins@gmail.com
 */
@Service("persistService")
public class PersistService {

	private static DatastoreService datastore = DatastoreServiceFactory
			.getDatastoreService();

	public <T> void save(T t) {
		try {
			this.getObjectElementFieldList(t).get(0)
					.set(t, datastore.put(this.getEntityFromObject(t)).getId());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public <T> void delete(T t) {
		try {
			datastore.delete(KeyFactory.createKey(t.getClass().getName(),
					(Long) this.getObjectElementFieldList(t).get(0).get(t)));
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	public <T> void delete(Class<T> c, Object id) {
		datastore.delete(KeyFactory.createKey(c.getName(), (Long) id));
	}

	@SuppressWarnings("unchecked")
	public <T> PageModule<T> get(Class<T> c) {
		List<T> data = new ArrayList<T>();
		Query query = new Query(c.getName());
		for (Entity entity : datastore.prepare(query).asIterable()) {
			data.add((T) this.getObjectFromEntity(entity));
		}
		return new PageModule<T>(c, data);
	}

	public <T> T get(Class<T> c, Object id) {
		T result = null;
		try {
			result = this.getObjectFromEntity(datastore.get(KeyFactory
					.createKey(c.getName(), (Long) id)));
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return result;
	}

	public User getUser(String email, String password) {
		List<User> data = new ArrayList<User>();
		Query query = new Query(User.class.getName());
		query.addFilter("email", FilterOperator.EQUAL, email);
		query.addFilter("password", FilterOperator.EQUAL, password);
		for (Entity entity : datastore.prepare(query).asIterable()) {
			data.add((User) this.getObjectFromEntity(entity));
		}
		if (null == data || data.size() < 1) {
			return null;
		}
		if (data.size() > 1) {
			throw new RuntimeException("user repeat");
		}
		return data.get(0);
	}

	@SuppressWarnings("unchecked")
	private <T> T getObjectFromEntity(Entity entity) {
		try {
			if (null == entity) {
				return null;
			}
			T result = (T) Class.forName(entity.getKind()).newInstance();
			List<Field> obf = this.getObjectElementFieldList(result);
			obf.get(0).set(result, entity.getKey().getId());
			for (Field f : obf) {
				if (entity.hasProperty(f.getName())) {
					Object value = entity.getProperty((f.getName()));
					if (null == value) {
						continue;
					}
					if (value instanceof Text) {
						value = ((Text) value).getValue();
					} else if (value instanceof Blob) {
						value = ((Blob) value).getBytes();
					}
					f.set(result, value);
				}
			}
			return result;
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private <T> Entity getEntityFromObject(T t) {
		Entity result = null;
		try {
			List<Field> obf = this.getObjectElementFieldList(t);
			Long key = (Long) obf.get(0).get(t);
			if (null == key || 0l == key) {
				result = new Entity(t.getClass().getName());
			} else {
				result = new Entity(t.getClass().getName(), key);
			}
			for (Field f : obf) {
				Object value = f.get(t);
				if (null == value) {
					continue;
				}
				if (value instanceof String && ((String) value).length() > 500) {
					value = new Text((String) value);
				} else if (value instanceof byte[]) {
					value = new Blob((byte[]) value);
				}
				result.setProperty(f.getName(), value);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		return result;
	}

	private <T> List<Field> getObjectElementFieldList(T t) {
		if (null == t) {
			throw new RuntimeException(new NullPointerException());
		}
		Annotation annotation = t.getClass().getAnnotation(Persistent.class);
		if (null == annotation) {
			throw new RuntimeException("Class:" + t.getClass().getSimpleName()
					+ " is not a persistent class");
		}
		int keyNumber = 0;
		List<Field> result = new ArrayList<Field>();
		for (Field f : t.getClass().getDeclaredFields()) {
			f.setAccessible(true);
			if (null != f.getAnnotation(Key.class)) {
				if (null != f.getAnnotation(Element.class)) {
					throw new RuntimeException("Class:"
							+ t.getClass().getSimpleName() + "-Field:"
							+ f.getName() + " is already as a Key");
				}
				if (!f.getType().equals(Long.class)) {
					throw new RuntimeException("Class:"
							+ t.getClass().getSimpleName() + "-Field:"
							+ f.getName() + " must be Class Long");
				}
				if (keyNumber > 0) {
					throw new RuntimeException("Class:"
							+ t.getClass().getSimpleName() + "-Field:"
							+ f.getName() + " has more than one Key");
				}
				result.add(0, f);
				keyNumber++;
			} else if (null != f.getAnnotation(Element.class)) {
				result.add(f);
			}
		}
		if (keyNumber == 0) {
			throw new RuntimeException("Class:" + t.getClass().getSimpleName()
					+ " does not have a Key field");
		}
		if (result.size() <= 1) {
			throw new RuntimeException("Class:" + t.getClass().getSimpleName()
					+ " does not have a need persist element field");
		}
		return result;
	}
}